#ifndef CPP_PROCESS_PROCESS_HPP
#define CPP_PROCESS_PROCESS_HPP

#include "config.hpp"
#include "error.hpp"
#include "io.hpp"

#include <istream>
#include <ostream>

NAMESPACE_STD
NAMESPACE_PROCESS

class Process
{

public:
#if defined(CPP_POSIX_API) 
    typedef pid_t PID; 
#elif defined(CPP_WINDOWS_API) 
    typedef DWORD PID; 
#endif

	Process(PID id, FileHandle fhstdin, FileHandle fhstdout, FileHandle fhstderr)  : id_(id)
    {
        if (fhstdin.valid())
            stdin_ = new ProcessOstream(fhstdin);
        if (fhstdout.valid())
            stdout_ = (new ProcessIstream(fhstdout));
        if (fhstderr.valid())
            stderr_ = (new ProcessIstream(fhstderr));
    }

	virtual ~Process()
	{
		if (stdin_)
		{
			delete stdin_;
			stdin_ = NULL;
		}
		if (stdout_)
		{
			delete stdout_;
			stdout_ = NULL;
		}
		if (stdout_)
		{
			delete stdout_;
			stdout_ = NULL;
		}
	}
public:
	ostream& GetOutputStream() {return *stdin_;}
	istream& GetInputStream()  {return *stdout_;}
	istream& GetErrorStream()  {return *stderr_;}
	PID      id()              {return id_;}
	virtual int WaitFor() = 0;
	virtual int ExitValue() = 0;
	virtual void Destroy() = 0;
protected:
	void CloseAllStreams()
	{
		// To do;
	}
protected:
	ostream* stdin_;
    istream* stdout_;
    istream* stderr_;
private:
	PID id_;
};

#if defined (CPP_POSIX_API)
#include <cerrno>
#include <sys/wait.h>
#include <sys/types.h>
#include <signal.h> 

class PosixProcess : Process
{
public:
	PosixProcess(Process::PID id, FileHandle fhstdin, FileHandle fhstdout, FileHandle fhstderr)
		: Process(id, fhstdin, fhstdout, fhstderr) {}

	virtual int WaitFor()
	{ 
        int status; 
        if (waitpid(id(), &status, 0) == -1) 
            throw SystemError(errno, "process::wait: waitpid(2) failed");
        return (status); 
	}


	virtual void Destroy()
	{
        if (kill(id(), true ? SIGKILL : SIGTERM) == -1) 
            throw SystemError(errno, "process::terminate: kill(2) failed");
	}
};

/* --------------------------------- Windows Process -------------------------------- */
#elif defined (CPP_WINDOWS_API)
#include <cstdlib> 
#include <windows.h> 

class WinProcess : Process
{
public:
	WinProcess(const PROCESS_INFORMATION &pi, FileHandle fhstdin, FileHandle fhstdout, FileHandle fhstderr)
		:Process(pi.dwProcessId, fhstdin, fhstdout, fhstderr),
		 pi_(pi), thread_handle_(pi.hThread)
    {
    }

	virtual int WaitFor()
	{
        ::WaitForSingleObject(pi.hProcess, INFINITE);
        DWORD code; 
        if (!::GetExitCodeProcess(pi.hProcess, &code))
            throw SystemError(::GetLastError(), "process::waitFor: GetExitCodeProcess failed"); 
        return (int)(code);
	}


	virtual void Destroy()
	{
		HANDLE handle = ::OpenProcess(PROCESS_TERMINATE, FALSE, id());
        if (handle == NULL)
             throw SystemError(::GetLastError(), "process::destroy: OpenProcess failed"); 
        if (!::TerminateProcess(handle, EXIT_FAILURE))
        { 
            ::CloseHandle(handle);
            throw SystemError(::GetLastError(), "process::destroy: TerminateProcess failed"); 
        } 
        if (!::CloseHandle(handle))
            throw SystemError(::GetLastError(), "process::destroy: CloseHandle failed"); 
	}
private:
	PROCESS_INFORMATION pi_;
	FileHandle thread_handle_;
};

#else
#  error "Unsupported platform."
#endif

END_NAMESPACE
END_NAMESPACE


#endif
